package distributedbank.client;

import distributedbank.Wrapper;
import distributedbank.responses.*;
import distributedbank.requests.*;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.*;
import javax.swing.text.*;
import java.text.*;

/**
 * ClientUI is a mock ATM. Its JvmId equals to the corresponding bank branch's
 * JvmId + 4.
 */
public class ClientUI implements ActionListener
{
    public ClientUI( int JvmNumber ) {
        int mod = JvmNumber % 4;
        // branch number from 1 - 4
        branchNumber = (mod == 0 ? 4 : mod);
        initComponents();
    }

    /**
     * Window Size
     */
    private static final int width = 500;

    /**
     * Window Size
     */
    private static final int height = 600;

    /**
     * Four branches in total, branchNumber = 1 through 4
     */
    private int branchNumber;

    /**
     * Each ClientUI generates distinct serialNumbers. This one is the number of
     * serialNumbers this ClientUI generated.
     */
    static int serialCount = 0;

    private MaskFormatter numberOf2;

    private MaskFormatter numberOf5;

    private JFormattedTextField accountNumberArea;

    private JFormattedTextField transferBranchArea;

    private JFormattedTextField transferAccountArea;

    private JTextField amountIntArea;

    private JFormattedTextField amountFloatArea;

    private JTextField serialArea;
    
    private JFrame frame;

    private JTextArea hintArea;

    private JScrollPane hintAreaScr;

    private JPanel framePanel;

    private JPanel leftPanel;

    private JPanel rightPanel;

    private JTextArea statusArea;

    private JScrollPane statusAreaScr;

    private JPanel lowerPanel;

    private JPanel accountPanel;

    private JPanel transferPanel;

    private JPanel amountPanel;

    private JLabel accountNumberLabel;

    private JLabel branchNumberLabel;

    private JLabel transferToLabel;

    private JLabel amountLabel;

    private JLabel dotLabel;

    private JButton depositButton;

    private JPanel serialPanel;
    
    private JButton withdrawButton;

    private JButton queryButton;

    private JButton transferButton;
    
    private JLabel serialLabel;

    private final static String hint = "Hints:\nDeposit or Withdraw: \n"
            + "Input your account # and amount of money. \n"
            + "Then press Deposit or Withdraw button.\n\n"
            + "Query: \nInput your account #.\n "
            + "Then press Query button.\n\n "
            + "Transfer: \nInput your account #, \n"
            + "destination branch # and account #, \n" + "amount of money. \n"
            + "Then press Transfer button.";

    private void initComponents() {
        try {
            numberOf2 = new MaskFormatter( "##" );
            numberOf5 = new MaskFormatter( "#####" );
        } catch( ParseException pe ) {
        }
        serialArea = new JTextField(5);
        serialPanel = new JPanel();
        serialLabel = new JLabel("Serial Number");
		serialLabel.setHorizontalAlignment(JLabel.CENTER);
		serialPanel.add(serialArea);
        accountNumberArea = new JFormattedTextField( numberOf5 );
        transferBranchArea = new JFormattedTextField( numberOf2 );
        transferAccountArea = new JFormattedTextField( numberOf5 );
        amountIntArea = new JTextField( 5 );
        amountFloatArea = new JFormattedTextField( numberOf2 );
        frame = new JFrame( "Branch " + branchNumber );
        hintArea = new JTextArea( hint, 5, 1 );
        hintAreaScr = new JScrollPane( hintArea );
        hintArea.setEditable( false );
        leftPanel = new JPanel();
        rightPanel = new JPanel();
        statusArea = new JTextArea( 7, 1 );
        statusAreaScr = new JScrollPane( statusArea );
        statusArea.setEditable( false );
        lowerPanel = new JPanel();
        framePanel = new JPanel();
        accountPanel = new JPanel();
        transferPanel = new JPanel();
        amountPanel = new JPanel();
        accountNumberLabel = new JLabel( "Account #" );
        accountNumberLabel.setHorizontalAlignment( JLabel.CENTER );
        branchNumberLabel = new JLabel( "0" + Integer.toString( branchNumber ) );
        branchNumberLabel.setHorizontalAlignment( JLabel.CENTER );
        transferToLabel = new JLabel( "Transfer To" );
        transferToLabel.setHorizontalAlignment( JLabel.CENTER );
        amountLabel = new JLabel( "# of amount" );
        amountLabel.setHorizontalAlignment( JLabel.CENTER );
        dotLabel = new JLabel( "." );
        dotLabel.setHorizontalAlignment( JLabel.CENTER );
        depositButton = new JButton( "Deposit" );
        withdrawButton = new JButton( "Withdraw" );
        queryButton = new JButton( "Query" );
        transferButton = new JButton( "Transfer" );
        depositButton.addActionListener( this );
        withdrawButton.addActionListener( this );
        queryButton.addActionListener( this );
        transferButton.addActionListener( this );
        amountPanel.setLayout( new GridLayout( 1, 3 ) );
        amountIntArea.setHorizontalAlignment( JLabel.CENTER );
        amountPanel.add( amountIntArea );
        amountPanel.add( dotLabel );
        amountFloatArea.setText( "00" );
        amountFloatArea.setHorizontalAlignment( JLabel.CENTER );
        amountPanel.add( amountFloatArea );
        frame.setLayout( new BorderLayout() );
        frame.add( "South", hintAreaScr );
        frame.add( framePanel );
        framePanel.setLayout( new GridLayout( 1, 2 ) );
        framePanel.add( leftPanel );
        framePanel.add( rightPanel );
        leftPanel.setLayout( new GridLayout( 6, 1 ) );
        leftPanel.add( accountNumberLabel );
        leftPanel.add( accountPanel );
        leftPanel.add( transferToLabel );
        leftPanel.add( transferPanel );
        leftPanel.add( amountLabel );
        leftPanel.add( amountPanel );
        accountPanel.setLayout( new GridLayout( 1, 2 ) );
        accountPanel.add( branchNumberLabel );
        accountNumberArea.setHorizontalAlignment( JLabel.CENTER );
        accountPanel.add( accountNumberArea );
        transferPanel.setLayout( new GridLayout( 1, 2 ) );
        transferBranchArea.setHorizontalAlignment( JLabel.CENTER );
        transferPanel.add( transferBranchArea );
        transferAccountArea.setHorizontalAlignment( JLabel.CENTER );
        transferPanel.add( transferAccountArea );
        rightPanel.setLayout( new GridLayout( 2, 1 ) );
        rightPanel.add( statusAreaScr );
        rightPanel.add( lowerPanel );
        lowerPanel.setLayout( new GridLayout( 4, 1 ) );
        lowerPanel.add( depositButton );
        lowerPanel.add( withdrawButton );
        lowerPanel.add( queryButton );
        lowerPanel.add( transferButton );
        frame.setSize( width, height );
        frame.setVisible( true );
        frame.setResizable( false );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    }

    /**
     * Use the branchNumber as the last digit of the serialNumber With a
     * serialCount, there won't be same serialNumbers.
     * 
     * @return serial number
     */
    private int generateSerial() {
        // generate serial number
        // jvm 5-8
        int serial = branchNumber + serialCount;
        serialCount += 10;
        return serial;
    }

    /**
     * Read response message and show them on statusArea
     */
    public void readMessage( String str ) {
        this.statusArea.append( str + "\n" );
    }

    /**
     * ActionEvents happens when buttons are pressed.
     */
    public void actionPerformed( ActionEvent e ) {
        String s = e.getActionCommand();
        int serialNumber = generateSerial();
        try{
    		if(!(serialArea.getText() == null || serialArea.getText().equals(""))){
    			serialNumber = Integer.parseInt(serialArea.getText());
    		}
    	}catch(NumberFormatException nfe){
    		statusArea.append("Serial Number should be integer.\n");
    		return;
    	}
        /**
         * For Deposit\Withdraw\Query, srcAcctNumber means accountNumber
         */
        int srcAcctNumber;
        int amount;
        int dstAcctNumber;
        int dstId;
        try {
            srcAcctNumber = Integer.parseInt( accountNumberArea.getText() );
        } catch( java.lang.NumberFormatException nfe ) {
            statusArea.append( "Check your input account number and retry.\n" );
            return;
        }
        if( "Deposit".equals( s ) ) {
            // check input format
            try {
                amount = Integer.parseInt( amountFloatArea.getText() ) + 100
                        * Integer.parseInt( amountIntArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea
                        .append( "Check your input amount \n(especially the 2-digit "
                                + "\nfloating number)and retry.\n" );
                return;
            }
            // send message
            Wrapper wrapper = new Wrapper();
            Serializable message = new DepositRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4, amount );
            if( wrapper.sendMessage( message, branchNumber + 4, branchNumber ) ) {
                // send success, receive the balance response
                try {
                    ServerSocket serverSocket = new ServerSocket(
                            Wrapper.basePort + branchNumber + 4 );
                    // system suspends on the accept()?????????
                    Socket clientSocket = serverSocket.accept();
                    try {
                        ObjectInputStream in = new ObjectInputStream(
                                clientSocket.getInputStream() );
                        try {
                            Response response = (Response) in.readObject();
                            response.runResponse( this );
                        } catch( ClassNotFoundException eee ) {
                            System.out.println( eee );
                        } finally {
                            in.close();
                            clientSocket.close();
                            serverSocket.close();
                        }
                    } catch( IOException eee ) {
                        System.out.println( eee );
                    }
                } catch( IOException eee ) {
                    System.out.println( eee );
                }
            } else
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        } else if( "Withdraw".equals( s ) ) {
            try {
                amount = Integer.parseInt( amountFloatArea.getText() ) + 100
                        * Integer.parseInt( amountIntArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea
                        .append( "Check your input amount \n(especially the 2-digit "
                                + "floating number)\nand retry.\n" );
                return;
            }
            // send deposit request, deposit a negative amount of money.
            Wrapper wrapper = new Wrapper();
            Serializable message = new DepositRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4, -amount );
            if( wrapper.sendMessage( message, branchNumber + 4, branchNumber ) ) {
                try {
                    ServerSocket serverSocket = new ServerSocket(
                            Wrapper.basePort + branchNumber + 4 );
                    // system suspends on the accept()?????????
                    Socket clientSocket = serverSocket.accept();
                    try {
                        ObjectInputStream in = new ObjectInputStream(
                                clientSocket.getInputStream() );
                        try {
                            Response response = (Response) in.readObject();
                            response.runResponse( this );
                        } catch( ClassNotFoundException eee ) {
                            System.out.println( eee );
                        } finally {
                            in.close();
                            clientSocket.close();
                            serverSocket.close();
                        }
                    } catch( IOException eee ) {
                        System.out.println( eee );
                    }
                } catch( IOException eee ) {
                    System.out.println( eee );
                }
            } else
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        } else if( "Query".equals( s ) ) {
            Wrapper wrapper = new Wrapper();
            Serializable message = new QueryRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4 );
            if( wrapper.sendMessage( message, branchNumber + 4, branchNumber ) ) {
                try {
                    ServerSocket serverSocket = new ServerSocket(
                            Wrapper.basePort + branchNumber + 4 );
                    // system suspends on the accept()?????????
                    Socket clientSocket = serverSocket.accept();
                    try {
                        ObjectInputStream in = new ObjectInputStream(
                                clientSocket.getInputStream() );
                        try {
                            Response response = (Response) in.readObject();
                            response.runResponse( this );
                        } catch( ClassNotFoundException eee ) {
                            System.out.println( eee );
                        } finally {
                            in.close();
                            clientSocket.close();
                            serverSocket.close();
                        }
                    } catch( IOException eee ) {
                        System.out.println( eee );
                    }
                } catch( IOException eee ) {
                    System.out.println( eee );
                }
            } else
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        } else if( "Transfer".equals( s ) ) {
            // check input format
            try {
                amount = Integer.parseInt( amountFloatArea.getText() ) + 100
                        * Integer.parseInt( amountIntArea.getText() );
                dstAcctNumber = Integer
                        .parseInt( transferAccountArea.getText() );
                dstId = Integer.parseInt( transferBranchArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea.append( "Check your input account and\n"
                        + "amount (especially the 2-digit \n"
                        + "floating number)and retry.\n" );
                return;
            }
            Wrapper wrapper = new Wrapper();
            Serializable message = new TransferRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4, dstId,
                    dstAcctNumber, amount );
            if( wrapper.sendMessage( message, branchNumber + 4, branchNumber ) ) {
                try {
                    ServerSocket serverSocket = new ServerSocket(
                            Wrapper.basePort + branchNumber + 4 );
                    // system suspends on the accept()?????????
                    Socket clientSocket = serverSocket.accept();
                    try {
                        ObjectInputStream in = new ObjectInputStream(
                                clientSocket.getInputStream() );
                        try {
                            Response response = (Response) in.readObject();
                            response.runResponse( this );
                        } catch( ClassNotFoundException eee ) {
                            System.out.println( eee );
                        } finally {
                            in.close();
                            clientSocket.close();
                            serverSocket.close();
                        }
                    } catch( IOException eee ) {
                        System.out.println( eee );
                    }
                } catch( IOException eee ) {
                    System.out.println( eee );
                }
            } else
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        }
    }
}